﻿using System;
using System.Collections.ObjectModel;
using System.Data.Entity.Validation;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using NLog;
using SellAvi.Classes;
using SellAvi.Model;
using SellAvi.Properties;
using SellAvi.Schemas;
using SellAvi.Services;

namespace SellAvi.ViewModel
{
    public class AdsParserViewModel : ViewModelBase
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        public RelayCommand ParsePageCommand { get; set; }
        public RelayCommand RefreshUrlCommand { get; set; }
        public RelayCommand ClearSettingsCommand { get; set; }
        public RelayCommand ParseProfileCommand { get; set; }
        public RelayCommand<ToolTip> ShowTooltipCommand { get; set; }
        public WebClientEx Webclient { get; set; }

        private AvitoUser _currentAvitoUser;
        private string _currentProcessingPage;
        private CancellationTokenSource _cancellationToken;

        public string CurrentProcessingPage
        {
            get
            {
                Uri tempValue;
                if (Uri.TryCreate(_currentProcessingPage, UriKind.Absolute, out tempValue))
                {
                    //new Uri("https://www.avito.ru/").MakeRelativeUri(tempValue).ToString();
                    var segments = tempValue.Segments;
                    return segments.LastOrDefault();
                }
                return _currentProcessingPage;
            }
            set
            {
                _currentProcessingPage = value;
                RaisePropertyChanged("CurrentProcessingPage");
            }
        }

        private bool _parseInProgress;
        public bool ParseInProgress
        {
            get { return _parseInProgress; }
            set
            {
                _parseInProgress = value;
                RaisePropertyChanged("ParseInProgress");
            }
        }

        public ParserSchema SPS { get; set; }
        public string ParseUrl
        {
            get { return new Uri(SPS.SiteUrl).MakeRelativeUri(SPS.DefaultParseUrl).ToString(); }
            set
            {
                Uri tempValue;
                if (Uri.TryCreate(value, UriKind.Absolute, out tempValue))
                {
                    SPS.DefaultParseUrl = tempValue;
                    RaisePropertyChanged("ParseUrl");
                }

            }
        }

        private int _collectedAdsCounter;
        public int CollectedAdsCounter
        {
            get { return _collectedAdsCounter; }
            set
            {
                _collectedAdsCounter = value;
                RaisePropertyChanged("CollectedAdsCounter");
            }
        }

        private TRegion _choosenRegion;
        public TRegion ChoosenRegion
        {
            get { return _choosenRegion; }
            set
            {
                _choosenRegion = value;
                if (_choosenRegion != null)
                {
                    RefreshUrl();
                }
                RaisePropertyChanged("ChoosenRegion");

            }
        }
        private TRegion _choosenCity;
        public TRegion ChoosenCity
        {
            get { return _choosenCity; }
            set
            {
                _choosenCity = value;
                if (_choosenCity != null)
                {
                    RefreshUrl();
                }
                RaisePropertyChanged("ChoosenCity");
            }
        }

        private TCategory _choosenCategory;
        public TCategory ChoosenCategory
        {
            get { return _choosenCategory; }
            set
            {
                _choosenCategory = value;
                if (_choosenCategory != null)
                {
                    RefreshUrl();
                }
                RaisePropertyChanged("TFilteredParameters");
                RaisePropertyChanged("ChoosenCategory");
            }
        }

        private TParam _choosenParameter;
        public TParam ChoosenParameter
        {
            get { return _choosenParameter; }
            set
            {
                _choosenParameter = value;
                if (_choosenParameter != null)
                {
                    RefreshUrl();
                }
                RaisePropertyChanged("ChoosenParameter");
            }
        }

        public ObservableCollection<TParam> TFilteredParameters
        {
            get
            {
                if (ChoosenCategory != null)
                {
                    return new ObservableCollection<TParam>(ServiceProxy.GetParams.Where(x => x.CategoryId == ChoosenCategory.Id));
                }
                return null;
            }
        }

        public IDataAccessService ServiceProxy { get; set; }


        public AdsParserViewModel(IDataAccessService serviceProxy)
        {
            ServiceProxy = serviceProxy;
            ParseInProgress = false;
            SPS = new ParserSchema();

            Messenger.Default.Register<WebClientEx>(this, (receivedClient) => { this.Webclient = receivedClient; });
            Messenger.Default.Register<AvitoUser>(this, (receivedUser) => { _currentAvitoUser = receivedUser; });

            ParsePageCommand = new RelayCommand(StartParseTask);
            ParseProfileCommand = new RelayCommand(ParseProfile);
            RefreshUrlCommand = new RelayCommand(RefreshUrl);
            ClearSettingsCommand = new RelayCommand(ClearSettings);
            ShowTooltipCommand = new RelayCommand<ToolTip>(((t) => t.IsOpen = !t.IsOpen));
        }

        private void ParseProfile()
        {
            ClearSettings();
            this.SPS.DefaultParseUrl = new Uri("https://www.avito.ru/profile/items/active"); /*страница активных объявлений профиля*/
            this.SPS.XPathPublishUrl = "//div[@class='profile-item-content']/a[@href]"; /*индекс с объявлениями*/
            RaisePropertyChanged("ParseUrl");

        }

        private void RefreshUrl()
        {
            var r = ChoosenRegion != null ? ChoosenRegion.LatinName + "/" : String.Empty;
            r = ChoosenCity != null ? ChoosenCity.LatinName + "/" : r;
            var c = ChoosenCategory != null ? ChoosenCategory.LatinName + "/" : String.Empty;
            var p = ChoosenParameter != null ? ChoosenParameter.LatinName + "/" : String.Empty;

            if (String.IsNullOrEmpty(r) && String.IsNullOrEmpty(c) && String.IsNullOrEmpty(p))
            {
                ParseUrl = SPS.DefaultParseUrl.ToString();
            }
            else
            {
                ParseUrl = "https://www.avito.ru/" + r + c + p;
            }
        }
        private void ClearSettings()
        {
            SPS = new ParserSchema();
            ChoosenCategory = null;
            ChoosenRegion = null;
            ChoosenCity = null;
            ChoosenParameter = null;
            CollectedAdsCounter = 0;
        }

        private void StartParseTask()
        {
            if (Webclient != null)
            {
                if (!ParseInProgress)
                {
                    ParseInProgress = true;
                    _cancellationToken = new CancellationTokenSource();
                    Task.Factory.StartNew(_ => ParseCurrentUrl(), _cancellationToken.Token)
                        .ContinueWith(_ => { ParseInProgress = false; });
                }
                else
                {
                    _cancellationToken.Cancel();
                }
            }
            else
            {
                MessageBox.Show("Для выполнения операции необходима авторизация в программе!", "Требуется авторизация",
                    MessageBoxButton.OK, MessageBoxImage.Asterisk);
            }
        }


        private void ParseCurrentUrl()
        {

            var app = new AvitoParserProcessor(Webclient, SPS);
            var indexItems = app.DownloadIndexUrlsAsync(SPS.DefaultParseUrl).Result;

            foreach (var item in indexItems)
            {
                Logger.Trace("Обработка товара со страницы: {0}", item.AdUrl);
                CurrentProcessingPage = item.AdUrl.ToString();
                Task.Delay(TimeSpan.FromMilliseconds(Settings.Default.ParserPolling * 1000), _cancellationToken.Token).Wait();
                try
                {
                    //Создание продукта из тела страницы с товаром
                    //var product = app.GetAvitoProductByLinkAsync(link).Result;
                    var product = app.GetAvitoProductByLink(item.AdUrl);

                    if (!Settings.Default.ParserAllowDuplicates && ServiceProxy.GetProducts().Any(x => x.ParsedFromUrl == product.ParsedFromUrl))
                    {
                        Logger.Info("Пропущен дубликат объявления по ссылке: {0}", item.AdUrl);
                        continue;
                    }

                    //Установка категории для продукта
                    if (SetHeadCrumbsCategory(product))
                    {
                        //Назначение владельца товара
                        product.AvitoUser = _currentAvitoUser;

                        //Установка даты сбора объявления
                        product.DateInsert = DateTime.Now;

                        if (Settings.Default.ParserDownloadImages)
                        {
                            //Подгрузка изображения к товару
                            app.DownloadProductImage(product);
                        }

                        if (Settings.Default.ParserDownloadPhones)
                        {
                            try
                            {
                                if (Settings.Default.ParserPhoneMode[0].Contains("не"))
                                {
                                    product.ParsedPhone = app.DownloadUserPhone(product);
                                }
                                else if ((Settings.Default.ParserPhoneMode[0].Contains("API")))
                                {
                                    product.ParsedPhone = app.DownloadUserPhone3(product);
                                }
                                else
                                {
                                    product.ParsedPhone = app.DownloadUserPhone2(product);
                                }

                            }
                            catch (Exception ex)
                            {
                                Logger.Error(ex, "Ошибка обработки телефонного номера");

                            }
                        }


                        //Создание товара
                        ServiceProxy.CreateProduct(product);

                        //Сохранение
                        ServiceProxy.SaveContext();

                    }

                    CollectedAdsCounter++;

                }
                catch (DbEntityValidationException ex)
                {
                    Logger.Error(ex, "Ограничения БД не позволяют обработать ошибочно-заполненный товар");
                }
                catch (AvitoParserProcessor.FormatChangedException ex)
                {
                    Logger.Error(ex, "Страница с товаром не может быть корректна обработана по причине изменения интерфейса web-страницы");
                    if (Settings.Default.ParserShowErrors)
                    {
                        var r = MessageBox.Show("Невозможно распознать web-страницу с объявлением, продолжить обработку объявлений?", "Обнаружен нестандартный Web-интерфейс страницы!", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
                        if (r == MessageBoxResult.No)
                        {
                            _cancellationToken.Cancel();
                        }
                        else
                        {
                            Settings.Default.ParserShowErrors = false;
                        }
                    }

                }
                catch (Exception ex)
                {
                    //throw;
                    Logger.Error(ex,
                        String.Format("Ошибка при обработке страницы с объявлением: {0}",
                            CurrentProcessingPage));
                    Console.WriteLine(ex);
                    continue;
                }
                finally
                {
                    // Запрос на отмену операции "парсинга"
                    if (_cancellationToken.IsCancellationRequested)
                    {
                        Logger.Debug("Запрос на отмену операции CancellationToken");
                        _cancellationToken.Token.ThrowIfCancellationRequested();
                    }

                }

            }

            Logger.Info("Сборщик объявлений окончил обработку {0}х объявлений", indexItems.Count);
            MessageBox.Show(String.Format("Обработано {0}х объявлений \r\nПерейдите в каталог для просмотра", indexItems.Count),
                "Страница с объявлениями обработана", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private bool SetHeadCrumbsCategory(AvitoProduct ap)
        {
            var elements = ap.BreadCrumbsCategory.Split('/');
            if (elements.Length > 2)
            {
                ap.Region = ServiceProxy.GetRegions.FirstOrDefault(x => x.LatinName == elements[1]);
                //1)
                var category = (from ctg in ServiceProxy.GetCategories
                                join elm in elements on ctg.LatinName equals elm
                                select ctg).FirstOrDefault();
                var param = (from prm in ServiceProxy.GetParams
                             join elm in elements on prm.LatinName equals elm
                             where category != null && prm.CategoryId == category.Id
                             select prm).FirstOrDefault();
                ap.Category = category;
                ap.Param = param;


                //2)
                //var linkedPair = (from prm in ServiceProxy.GetParams
                //                  join ctg in ServiceProxy.GetCategories on prm.CategoryId equals ctg.Id
                //                  where ctg.LatinName == elements[2] && prm.LatinName == elements[3]
                //                  select new { Categoty = ctg, Param = prm }).Single();
                //ap.Category = linkedPair.Categoty;
                //ap.Param = linkedPair.Param;

                //3)
                //ap.Category = ServiceProxy.GetCategories.FirstOrDefault(x => x.LatinName == elements[2]);
                //ap.Param = ServiceProxy.GetParams.FirstOrDefault(x => x.LatinName == elements[3]);
            }

            if (ap.Region == null || ap.Category == null)
            {
                Logger.Warn("Невозможно распознать вид товара: {0}", ap.BreadCrumbsCategory);
                return false;
            }
            Logger.Info("Товар отнесен к категории: {0} > {1}", ap.Category.CategoryName,
                (ap.Param == null ? "нет подкатегории" : ap.Param.ParamName));
            return true;


        }
    }
}
